package com.xc.disk.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xc.disk.bean.FolderBean;
import com.xc.disk.bean.NoticeBean;
import com.xc.disk.bean.SystemFolderBean;
import com.xc.disk.config.Constants;
import com.xc.disk.dto.FolderDto;
import com.xc.disk.entity.FileEntity;
import com.xc.disk.entity.FolderEntity;
import com.xc.disk.enums.RedisLock;
import com.xc.disk.exception.OperateException;
import com.xc.disk.info.*;
import com.xc.disk.mapper.FileMapper;
import com.xc.disk.mapper.FolderMapper;
import com.xc.disk.service.FileService;
import com.xc.disk.service.FolderService;
import com.xc.disk.service.UserService;
import com.xc.disk.utils.RedisUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Hashtable;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;

/**
 * <p>项目名称: Stars</p>
 * <p>描述: 文件夹服务实现类</p>
 * <P>创建人: 小程</p>
 * <P>创建人邮箱: 779943476@qq.com</p>
 * <p>创建时间: 2019/07/25</p>
 *
 * @version v1.0
 */
@Service
@Slf4j
public class FolderServiceImpl extends ServiceImpl<FolderMapper, FolderEntity> implements FolderService {

    @Autowired
    private FolderMapper folderMapper;
    @Autowired
    private FileMapper fileMapper;
    @Autowired
    private FolderService folderService;
    @Autowired
    private Constants constants;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private FileService fileService;
    @Autowired
    private UserService userService;

    @Override
    public FolderEntity createFolder(TokenInfo token, FolderBean folderBean) {
        FolderEntity folderEntity = new FolderEntity();
        folderEntity.setParentId(folderBean.getParentId());
        folderEntity.setName(folderBean.getName());
        folderEntity.setUserId(token.getUserId());
        FolderEntity entity = folderMapper.selectOne(new QueryWrapper<>(folderEntity));
        if (entity != null) {
            //不等于空，重命名文件
            String name = fileService.rename(entity.getName(), entity.getParentId(), token.getUserId());
            entity.setName(name);
            entity.setId(null);
            entity.setCreateTime(null);
            entity.setUpdateTime(null);
            entity.setVersion(null);
            if (!this.retBool(folderMapper.insert(entity))) {
                throw new OperateException("f030", "创建文件失败！");
            }
            return entity;
        }
        if (!this.retBool(folderMapper.insert(folderEntity))) {
            throw new OperateException("c001", "添加失败！");
        }
        return folderEntity;
    }

    @Override
    public FolderEntity updateFolder(TokenInfo token, String id, String name) {
        FolderEntity folderEntity = new FolderEntity();
        folderEntity.setId(id);
        folderEntity.setUserId(token.getUserId());
        folderEntity = folderMapper.selectOne(new QueryWrapper<>(folderEntity));
        folderEntity.setName(name);
        if (!this.updateById(folderEntity)) {
            throw new OperateException("f031", "修改文件失败！");
        }
        return folderEntity;
    }

    @Override
    public FolderDto getFolderFileList(TokenInfo token, String parentId) {
        FolderDto folderDto = new FolderDto();
        //查询目录
        FolderEntity folderEntity = new FolderEntity();
        folderEntity.setParentId(parentId);
        folderEntity.setUserId(token.getUserId());
        folderDto.setFolders(folderMapper.selectList(new QueryWrapper<>(folderEntity)));
        //查询文件
        FileEntity fileEntity = new FileEntity();
        fileEntity.setFolderId(parentId);
        fileEntity.setUserId(token.getUserId());
        folderDto.setFiles(fileMapper.selectList(new QueryWrapper<>(fileEntity)));
        return folderDto;
    }

    @Override
    @Transactional
    public void deleteFolderAndFileByIds(TokenInfo token, List<String> folderIds, List<String> fileIds) {
        long size = 0;
        String parentId = null;
        Map<String, FileEntity> hashCodeMap = new Hashtable<>();
        if (fileIds != null) {
            for (String fileId : fileIds) {
                FileEntity fileEntity = fileService.getFileById(fileId);
                hashCodeMap.put(fileEntity.getHashCode(), fileEntity);
                parentId = fileEntity.getFolderId();
                size += fileEntity.getSize();
                if (!this.retBool(fileMapper.deleteById(fileId))) {
                    throw new OperateException("f006", "文件删除失败！");
                }
            }
        }
        if (folderIds != null) {
            for (String folderId : folderIds) {
                FolderEntity folderEntity = getById(folderId);
                parentId = folderEntity.getParentId();
                size += folderEntity.getSize();
                if (!this.retBool(folderMapper.deleteById(folderId))) {
                    throw new OperateException("f006", "目录删除失败！！");
                }
                folderService.subdirectoryHandle(folderId, null, (subdirectoryInfo) -> {
                    if (subdirectoryInfo.getIsFile()) {
                        List<FileEntity> files = subdirectoryInfo.getFiles();
                        for (FileEntity fileEntity : files) {
                            hashCodeMap.put(fileEntity.getHashCode(), fileEntity);
                            if (!this.retBool(fileMapper.deleteById(fileEntity.getId()))) {
                                throw new OperateException("f006", "文件删除失败！");
                            }
                        }
                    } else {
                        FolderEntity folder = subdirectoryInfo.getFolder();
                        if (!this.retBool(folderMapper.deleteById(folder.getId()))) {
                            throw new OperateException("f006", "目录删除失败！");
                        }
                    }
                });
            }
        }
        //删除文件源
        List<String> hashCodes = new ArrayList<>(hashCodeMap.keySet());
        deleteSourceInfo(hashCodes);
        //计算使用空间
        userService.computeUseSpace(token.getUserId(), size, false);
        //计算上级目录大小
        if (parentId != null) {
            subtractFolderSize(parentId, size);
        }
    }

    @Override
    public List<FolderEntity> getSubsetFolders(List<FolderEntity> folders) {
        List<FolderEntity> entities = new ArrayList<>();
        if (folders != null) {
            for (FolderEntity folderEntity : folders) {
                entities.add(folderEntity);
                List<FolderEntity> fileList = folderService.getFoldersByParentId(folderEntity.getId());
                entities.addAll(getSubsetFolders(fileList));
            }
        }
        return entities;
    }

    @Override
    public List<FolderEntity> getParentFolders(String folderId) {
        List<FolderEntity> folders = new ArrayList<>();
        FolderEntity folderEntity = folderMapper.selectById(folderId);
        if (folderEntity != null) {
            folders = getParentFolders(folderEntity.getParentId());
            folders.add(folderEntity);
        }
        return folders;
    }

    @Override
    public List<FolderEntity> getFoldersByParentId(String parentId) {
        FolderEntity entity = new FolderEntity();
        entity.setParentId(parentId);
        return folderMapper.selectList(new QueryWrapper<>(entity));
    }

    @Override
    public String createFolder(List<String> folderIds, int index, String userId, String parentId) {
        synchronized ("") {
            if (folderIds.size() <= index) {
                return parentId;
            }
            String folder = folderIds.get(index);
            FolderEntity folderEntity = new FolderEntity();
            folderEntity.setUserId(userId);
            folderEntity.setName(folder);
            folderEntity.setParentId(parentId);
            FolderEntity entity = folderMapper.selectOne(new QueryWrapper<>(folderEntity));
            if (entity == null) {
                if (!this.retBool(folderMapper.insert(folderEntity))) {
                    throw new OperateException("f028", "创建目录失败！");
                }
                index++;
                return this.createFolder(folderIds, index, userId, folderEntity.getId());
            } else {
                index++;
                return this.createFolder(folderIds, index, userId, entity.getId());
            }
        }
    }

    @Override
    public boolean seekFolderCrossPoint(String folderId, String newFolderId, CrossPointInfo
            crossPointInfo, Consumer<CrossPointInfo> action) {
        //创建内部使用对象
        if (crossPointInfo == null) {
            crossPointInfo = new CrossPointInfo();
            crossPointInfo.setFolders(new ArrayList<>());
            crossPointInfo.setNewFolders(new ArrayList<>());
        }
        List<FolderEntity> folders = crossPointInfo.getFolders();
        List<FolderEntity> newFolders = crossPointInfo.getNewFolders();
        boolean bool = false;
        //查询旧的上级目录
        FolderEntity folder = null;
        if (folderId != null) {
            folder = folderMapper.selectById(folderId);
            if (folder != null) {
                folders.add(folder);
                bool = true;
            }
        }
        //查询新的上级目录
        FolderEntity newFolder = null;
        if (newFolderId != null) {
            newFolder = folderMapper.selectById(newFolderId);
            if (newFolder != null) {
                newFolders.add(newFolder);
                bool = true;
            }
        }
        //判断新旧目录是否存在
        if (bool) {
            if (folder == null) {
                //新目录存在，递归继续查询上级目录
                if (seekFolderCrossPoint(null, newFolder.getParentId(), crossPointInfo, action)) {
                    return true;
                }
                if (folders.size() == 0) {
                    action.accept(new CrossPointInfo(new ArrayList<>(), newFolders));
                    return true;
                } else {
                    countFolderCrossPoint(folders, newFolders, action);
                    return true;
                }
            } else if (newFolder == null) {
                //旧目录存在，递归继续查询上级目录
                if (seekFolderCrossPoint(folder.getParentId(), null, crossPointInfo, action)) {
                    return true;
                }
                if (newFolders.size() == 0) {
                    action.accept(new CrossPointInfo(folders, new ArrayList<>()));
                    return true;
                } else {
                    countFolderCrossPoint(folders, newFolders, action);
                    return true;
                }
            } else {
                //新旧目录都存在，递归继续查询上级目录
                if (seekFolderCrossPoint(folder.getParentId(), newFolder.getParentId(), crossPointInfo, action)) {
                    return true;
                } else {
                    countFolderCrossPoint(folders, newFolders, action);
                    return true;
                }
            }
        } else {
            return false;
        }
    }

    @Override
    public void subdirectoryHandle(String parentId, String newParentId, Consumer<SubdirectoryInfo> action) {
        //处理文件
        List<FileEntity> files = fileService.getFileByFolderId(parentId);
        if (files != null && files.size() != 0) {
            SubdirectoryInfo subdirectoryInfo = new SubdirectoryInfo();
            subdirectoryInfo.setIsFile(true);
            subdirectoryInfo.setFiles(files);
            subdirectoryInfo.setNewParentId(newParentId);
            action.accept(subdirectoryInfo);
        }
        //判断是否有下级目录，有继续递归出处理
        List<FolderEntity> folders = folderService.getFoldersByParentId(parentId);
        if (folders != null) {
            for (FolderEntity folder : folders) {
                SubdirectoryInfo subdirectoryInfo = new SubdirectoryInfo();
                String id = folder.getId();
                subdirectoryInfo.setIsFile(false);
                subdirectoryInfo.setFolder(folder);
                subdirectoryInfo.setNewParentId(newParentId);
                action.accept(subdirectoryInfo);
                subdirectoryHandle(id, subdirectoryInfo.getFolder().getId(), action);
            }
        }
    }

    @Override
    public void subtractFolderSize(String folderId, long size) {
        //计算减去文件大小
        if (folderId != null) {
            List<FolderEntity> folderEntityList = folderService.getParentFolders(folderId);
            if (folderEntityList.size() != 0) {
                //计算上级目录大小
                for (FolderEntity folderEntity1 : folderEntityList) {
                    folderEntity1.setSize(folderEntity1.getSize() - size);
                }
                if (!folderService.updateBatchById(folderEntityList)) {
                    throw new OperateException("c002", "修改失败！");
                }
            }
        }
    }

    @Override
    public void addFolderSize(String parentId, long size) {
        //计算加上文件大小
        if (parentId != null) {
            List<FolderEntity> folderEntityList = folderService.getParentFolders(parentId);
            //计算上级目录大小
            for (FolderEntity folderEntity1 : folderEntityList) {
                folderEntity1.setSize(folderEntity1.getSize() + size);
            }
            if (folderEntityList.size() != 0) {
                if (!folderService.updateBatchById(folderEntityList)) {
                    throw new OperateException("c002", "修改失败！");
                }
            }
        }
    }

    @Override
    public boolean verifyFolder(String parentId, String newParentId) {
        FolderEntity folder = folderMapper.selectById(parentId);
        if (folder != null) {
            if (folder.getId().equals(newParentId)) {
                return true;
            } else {
                return verifyFolder(folder.getParentId(), newParentId);
            }
        } else {
            return false;
        }
    }

    @Override
    public FolderEntity getRootFolders(String id) {
        FolderEntity folder = folderMapper.selectById(id);
        if (folder != null) {
            FolderEntity folderEntity = getRootFolders(folder.getParentId());
            if (folderEntity != null) {
                return folderEntity;
            } else {
                return folder;
            }
        } else {
            return null;
        }
    }

    @Override
    public void deleteSourceInfo(List<String> hashCodes) {
        verifyHashCode(hashCodes, 1000, (stringList) -> {
            log.info("删除文件hash值:{}", stringList);
            //删除注册记录
            List<DeleteInfo> deleteInfos = new ArrayList<>();
            List<NoticeBean> noticeBeans = RedisUtils.get(constants.getFileServerKey());
            if (noticeBeans == null) {
                throw new OperateException("", "删除错误，没有文件服务！");
            }
            for (NoticeBean noticeBean : noticeBeans) {
                List<HashInfo> hashInfos = new ArrayList<>();
                for (HashInfo hashInfo : noticeBean.getHashInfos()) {
                    for (String hashCode : stringList) {
                        if (hashInfo.getHashCode().equals(hashCode)) {
                            hashInfos.add(hashInfo);
                        }
                    }
                }
                List<String> codes = new ArrayList<>();
                for (HashInfo hashInfo : hashInfos) {
                    codes.add(hashInfo.getHashCode());
                    noticeBean.getHashInfos().remove(hashInfo);
                    for (SystemFolderBean systemFolderBean : noticeBean.getFolderBeans()) {
                        if (systemFolderBean.getPath().equals(hashInfo.getPath())) {
                            systemFolderBean.setAvailableSpace(systemFolderBean.getAvailableSpace() + hashInfo.getSize());
                        }
                    }
                }
                DeleteInfo deleteFileInfo = new DeleteInfo();
                deleteFileInfo.setNoticeBean(noticeBean);
                deleteFileInfo.setHashCodes(codes);
                deleteInfos.add(deleteFileInfo);
            }
            RedisUtils.set(constants.getFileServerKey(), noticeBeans);
            //删除文件
            for (DeleteInfo deleteInfo : deleteInfos) {
                if (deleteInfo.getHashCodes().size() != 0) {
                    deleteSource(deleteInfo.getHashCodes(),
                            deleteInfo.getNoticeBean().getLocalUrl(),
                            deleteInfo.getNoticeBean().getSecret());
                }
            }
        });
    }

    @Override
    public void verifyHashCode(List<String> hashCodes, int batchSize, Consumer<List<String>> action) {
        try {
            if (!RedisUtils.lock(RedisLock.FILE_LOCK.getKey(), System.currentTimeMillis())) {
                throw new OperateException("", "文件操作超时！");
            }
            sectionHandle(hashCodes, batchSize, (codes) -> {
                QueryWrapper<FileEntity> queryWrapper = new QueryWrapper<>();
                queryWrapper.in("hash_code", codes);
                List<FileEntity> fileEntities = fileMapper.selectList(queryWrapper);
                if ((fileEntities == null || fileEntities.size() == 0)) {
                    action.accept(codes);
                } else {
                    List<String> strings = new ArrayList<>();
                    for (String hashCode : codes) {
                        boolean bool = false;
                        if (fileEntities.size() != 0) {
                            for (FileEntity fileEntity : fileEntities) {
                                if (fileEntity.getHashCode().equals(hashCode)) {
                                    bool = true;
                                }
                            }
                        }
                        if (!bool) {
                            strings.add(hashCode);
                        }
                    }
                    action.accept(strings);
                }
            });
        } finally {
            RedisUtils.unlock(RedisLock.FILE_LOCK.getKey());
        }
    }

    @Override
    public void deleteSource(List<String> hashCodes, String localUrl, String secret) {
        sectionHandle(hashCodes, 50, (codes) -> {
            log.info("删除文件数:{}", codes.size());
            StringBuilder buffer = new StringBuilder();
            buffer.append(localUrl);
            buffer.append(constants.getDeleteFileUrl());
            for (int i = 0; i < codes.size(); i++) {
                String hashCode = codes.get(i);
                buffer.append(hashCode);
                if (i < codes.size() - 1) {
                    buffer.append(",");
                }
            }
            buffer.append("?secret=");
            buffer.append(secret);
            try {
                ResponseEntity<Map> resultEntity = restTemplate.exchange(buffer.toString(), HttpMethod.DELETE, null, Map.class);
                if (resultEntity.getStatusCodeValue() != 200) {
                    Map<String, String> body = resultEntity.getBody();
                    throw new OperateException(body.get("code"), body.get("message"));
                }
            } catch (Exception e) {
                throw new OperateException("f053", "删除文件源失败！");
            }
        });
    }

    /**
     * 分批次处理文件
     *
     * @param hashCodes 全部文件
     * @param batchSize 多少要数据处理一次
     * @param action    处理回调
     */
    private void sectionHandle(List<String> hashCodes, int batchSize, Consumer<List<String>> action) {
        List<String> hash = new ArrayList<>();
        for (int i = 0; i < hashCodes.size(); i++) {
            if (i >= batchSize && i % batchSize == 0) {
                action.accept(hash);
                hash = new ArrayList<>();
            }
            hash.add(hashCodes.get(i));
            if (i == hashCodes.size() - 1) {
                action.accept(hash);
            }
        }
    }

    /**
     * 计算目录交叉点数据
     *
     * @param folders    旧目录数据
     * @param newFolders 新目录数据
     * @param action     回调
     */
    private void countFolderCrossPoint(List<FolderEntity> folders, List<FolderEntity> newFolders, Consumer<CrossPointInfo> action) {
        int i;
        int j = 0;
        boolean b = false;
        for (i = 0; i < folders.size(); i++) {
            for (j = 0; j < newFolders.size(); j++) {
                if (folders.get(i).getParentId().equals(newFolders.get(j).getParentId())) {
                    b = true;
                    break;
                }
            }
            if (b) {
                break;
            }
        }
        if (b) {
            List<FolderEntity> folderList = new ArrayList<>();
            for (int a = 0; a < i + 1; a++) {
                folderList.add(folders.get(a));
            }
            List<FolderEntity> newFolderList = new ArrayList<>();
            for (int a = 0; a < j + 1; a++) {
                newFolderList.add(newFolders.get(a));
            }
            action.accept(new CrossPointInfo(folderList, newFolderList));
        }
    }
}
